package xyz.ck.commons.interval;

public class ContinousIntervalUnit<T> extends IntervalUnit<T> {

	public ContinousIntervalUnit(boolean leftClose, T min, T max,
			boolean rightClose) {
		super(min, max);
		this.leftClose = leftClose;
		this.rightClose = rightClose;
		if (this.isEmpty()) {
			throw new IntervalException(
					"Can not instanceof an empty Interval: "
							+ this.getStringValue());
			
		}
	}

	/**
	 * This method must be override while extends.
	 * 
	 * @param leftClose
	 * @param min
	 * @param max
	 * @param rightClose
	 * @return
	 */
	public ContinousIntervalUnit<T> newInstance(boolean leftClose, T min,
			T max, boolean rightClose) {
		return new ContinousIntervalUnit<T>(leftClose, min, max, rightClose);
	}

	/**
	 * 
	 * @see <code> ContinousIntervalUnit.newInstance(boolean,T, T, boolean)</code>
	 */
	@Override
	public IntervalUnit<T> newInstance(T min, T max) {
		throw new IntervalException(
				"Continous Interval should newInstance using xyz.ck.commons.interval.ContinousIntervalUnit.newInstance(boolean, T, T, boolean)");
	}

	/**
	 * true ������ false ������,Ĭ�ϱ�
	 */
	protected boolean leftClose = true;

	protected boolean rightClose = true;
	/**
	 * 
	 */
	private static final long serialVersionUID = 7873857723915042242L;

	@Override
	public boolean isContinuous() {
		return true;
	}

	@Override
	public String getStringValue() {
		return (this.leftClose ? "[" : "(") + this.min().toString() + ","
				+ this.max.toString() + (this.rightClose ? "]" : ")");
	}

	public boolean eq(T a, boolean closeA, T b) {
		return this.eq(a, b) && closeA;
	}

	public boolean gteq(T a, boolean closeA, T b) {
		int compare = this.compare(a, b);
		return compare > 0 || (compare == 0 && closeA);
	}

	public boolean lteq(T a, boolean closeA, T b) {
		int compare = this.compare(a, b);
		return compare < 0 || (compare == 0 && closeA);
		// return this.lt(a, b)|| this.eq(a, closeA, b, closeB);
	}

	@Override
	public boolean isIn(T t) {
		int leftCode = this.compare(this.min(), t);

		int rightCode = this.compare(this.max(), t);

		boolean left = leftCode < 0 || (leftCode == 0 && this.leftClose);

		boolean right = rightCode > 0 || (rightCode == 0 && this.rightClose);

		return left && right;
	}

	@Override
	public Interval<T> intersect(Interval<T> interval) {
		if (interval.isGroup()) {
			IntervalGroup<T> group = (IntervalGroup<T>) interval;
			return group.intersect(this);
		}
		if (!this.isIntersect(interval)) {
			return null;
		}

		ContinousIntervalUnit<T> unit = (ContinousIntervalUnit<T>) interval;

		boolean leftC = false;
		T maxMin = null;
		if (this.lt(this.min(), unit.min())) {
			leftC = unit.leftClose;
			maxMin = unit.min();
		} else if (this.eq(this.min(), unit.min())) {
			if (!this.leftClose || !unit.leftClose) {
				leftC = false;
			} else {
				leftC = true;
			}
			maxMin = this.min();
		} else {
			leftC = this.leftClose;
			maxMin = this.min();
		}

		boolean rightC = false;
		T minMax = null;
		if (this.gt(this.max(), unit.max())) {
			rightC = unit.rightClose;
			minMax = unit.max();
		} else if (this.eq(this.max(), unit.max())) {
			if (!this.rightClose || !unit.rightClose) {
				rightC = false;
			} else {
				rightC = true;
			}
			minMax = this.max();
		} else {
			rightC = this.rightClose;
			minMax = this.max();
		}

		return newInstance(leftC, maxMin, minMax, rightC);
	}

	boolean isContains(ContinousIntervalUnit<T> unit) {

		int leftCode = this.compare(this.min(), unit.min());

		int rightCode = this.compare(this.max(), unit.max());

		boolean leftLeft = leftCode < 0
				|| (leftCode == 0 && !(!this.leftClose && unit.leftClose));

		boolean rightRight = rightCode > 0
				|| (rightCode == 0 && !(!this.rightClose && unit.rightClose));

		// boolean leftLeft = this.lt(this.min(), unit.min())
		// || (this.eq(this.min(), unit.min()) && !(!this.leftClose &&
		// unit.leftClose));
		//
		// boolean rightRight = this.gt(this.max(), unit.max())
		// || (this.eq(this.max(), unit.max()) && !(!this.rightClose &&
		// unit.rightClose));

		return leftLeft && rightRight;
	}

	@Override
	public boolean isContains(Interval<T> interval) {
		if (interval.isGroup()) {
			IntervalGroup<T> group = (IntervalGroup<T>) interval;
			return group.isContains(this);
		}
		ContinousIntervalUnit<T> unit = (ContinousIntervalUnit<T>) interval;

		return this.isContains(unit);
	}

	// boolean left = this.gteq(this.min(), unit.min());
	// if (left) {
	// return false;
	// }
	// boolean right = this.lteq(this.max(), unit.max());
	// if (right) {
	// return false;
	// }
	// return true;

	boolean isIntersect(ContinousIntervalUnit<T> unit) {

		int leftRightCode = this.compare(this.min(), unit.max());

		int rightLeftCode = this.compare(this.max(), unit.min());

		boolean isSeperate = leftRightCode > 0
				|| (leftRightCode == 0 && (!this.leftClose || !unit.rightClose))
				||

				rightLeftCode < 0
				|| (rightLeftCode == 0 && (!this.rightClose || !unit.leftClose));
		return !isSeperate;

		// boolean left = this.gt(this.min(), unit.max())
		// || ((!this.leftClose || !unit.rightClose) && this.eq(
		// this.min(), unit.max()));
		//
		// boolean right = this.lt(this.max(), unit.min())
		// || ((!this.rightClose || !unit.leftClose) && this.eq(
		// this.max(), unit.min()));
		//
		//
		//
		// return !(left || right);
	}

	@Override
	public boolean isIntersect(Interval<T> interval) {
		if (interval.isGroup()) {
			IntervalGroup<T> group = (IntervalGroup<T>) interval;
			return group.isIntersect(this);
		}
		ContinousIntervalUnit<T> unit = (ContinousIntervalUnit<T>) interval;
		//
		// boolean left = this.gt(this.min(), unit.max())
		// || ((!this.leftClose || !unit.rightClose) && this.eq(
		// this.min(), unit.max()));
		//
		// boolean right = this.lt(this.max(), unit.min())
		// || ((!this.rightClose || !unit.leftClose) && this.eq(
		// this.max(), unit.min()));
		//
		// return !(left || right);

		return isIntersect(unit);
	}

	/**
	 * "Continous number has no next!"
	 */
	@Override
	public T next(T t, boolean greater) {
		throw new IntervalException("Continous number has no next!");
	}

	@Override
	public Interval<T> minusUnit(IntervalUnit<T> unit) {
		if (unit == null) {
			return this;
		}
		if (this.isContinuous() != unit.isContinuous()) {
			throw new IntervalException(
					"Continuous interval can not compare to an not continuous interval!");
		}
		if (!this.isIntersect(unit)) {
			return newInstance(this.leftClose, this.min(), this.max(),
					this.rightClose);
		}
		ContinousIntervalUnit<T> target = (ContinousIntervalUnit<T>) unit;
		if (target.isContains(this)) {// Ŀ����������˱�����
			return null;
		}
		int leftcode = this.compare(this.min(), target.min());

		int rightCode = this.compare(this.max(), target.max());

		boolean left = leftcode < 0
				|| (leftcode == 0 && this.leftClose && !target.leftClose);

		boolean right = rightCode > 0
				|| (rightCode == 0 && this.rightClose && !target.rightClose);

		if (left && right) {// Ŀ�����䱻��������ȫ�����������佫���ضϳ���������
			IntervalUnit<T> leftUnit = newInstance(this.leftClose, this.min(),
					target.min(), !target.leftClose);
			IntervalUnit<T> rightUnit = newInstance(!target.rightClose,
					target.max(), this.max(), this.rightClose);
			return IntervalGroup.instance(leftUnit, rightUnit);
		}

		if (!left && right) {// Ŀ�������뱾������벿���ཻ
			return newInstance(!target.rightClose, target.max(), this.max(),
					this.rightClose);

		}

		if (left && !right) {// Ŀ�������뱾�����ְ벿���ཻ
			return newInstance(this.leftClose, this.min(), target.min(),
					!target.leftClose);
		}

		throw new ClassCastException(this + " minus " + target
				+ " unbelievable case! ");

		// return super.minusUnit(unit);
	}

	@Override
	public Interval<T> union(Interval<T> target) {
		if (target == null) {
			return this;
		}
		if (this.isContinuous() != target.isContinuous()) {
			throw new IntervalException(
					"Continuous interval can not compare to an not continuous interval!");
		}

		ContinousIntervalUnit<T> unit = (ContinousIntervalUnit<T>) target;

		int leftRightcode = this.compare(this.min(), target.max());

		if (leftRightcode > 0
				|| (leftRightcode == 0 && !(this.leftClose || unit.rightClose))) {

			ContinousIntervalUnit<T> u1 = newInstance(this.leftClose,
					this.min(), this.max(), this.rightClose);
			ContinousIntervalUnit<T> u2 = newInstance(unit.leftClose,
					unit.min(), unit.max(), unit.rightClose);
			return IntervalGroup.instance(u2, u1);
		}
		int rightLeftCode = this.compare(this.max(), target.min());

		if (rightLeftCode < 0
				|| (rightLeftCode == 0 && !(this.rightClose || unit.leftClose))) {
			ContinousIntervalUnit<T> u1 = newInstance(this.leftClose,
					this.min(), this.max(), this.rightClose);
			ContinousIntervalUnit<T> u2 = newInstance(unit.leftClose,
					unit.min(), unit.max(), unit.rightClose);
			return IntervalGroup.instance(u1, u2);
		}
		int leftcode = this.compare(this.min(), target.min());

		boolean leftC = false;

		T minmin = null;

		if (leftcode < 0) {
			leftC = this.leftClose;
			minmin = this.min();
		} else if (leftcode == 0) {
			leftC = this.leftClose || unit.leftClose;
			minmin = this.min();
		} else {
			leftC = unit.leftClose;
			minmin = unit.min();
		}

		int rightCode = this.compare(this.max(), target.max());
		boolean rightC = false;

		T maxmax = null;
		if (rightCode > 0) {
			rightC = this.rightClose;
			maxmax = this.max();
		} else if (rightCode == 0) {
			rightC = this.rightClose || unit.rightClose;
			maxmax = this.max();
		} else {
			rightC = unit.rightClose;
			maxmax = unit.max();
		}

		return newInstance(leftC, minmin, maxmax, rightC);
	}

	public static void main(String[] args) {
		Interval<Double> x = new ContinousIntervalUnit<Double>(false, 1d, 2d,
				false);
		Interval<Double> y = new ContinousIntervalUnit<Double>(false, 2d, 4d,
				false);
		System.out.println(x.intersect(y));
	}
}
